package org.piwicode.bywordofmouth.net;

import com.google.common.base.Preconditions;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 *
 * @author pierre
 */
class CancellableExecutor extends ThreadPoolExecutor {
    private final static Object PLACE_HOLDER=new Object();
    private final ConcurrentMap<Cancellable, Object> runningCancellables = new ConcurrentHashMap<>();

    public CancellableExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    @Override
    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        throw new AssertionError("Use execute and pass an Cancellable");
    }

    @Override
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        throw new AssertionError("Use execute and pass an Cancellable");
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        //Cancellable commes from Executor.execute, Future comes from Executor.execute and Executor.submit        
        runningCancellables.put((Cancellable) r, PLACE_HOLDER);
        super.beforeExecute(t, r);
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        runningCancellables.remove(r, null);
        super.afterExecute(r, t);
    }

    @Override
    public List<Runnable> shutdownNow() {
        final List<Runnable> shutdownNow = super.shutdownNow();
        // New tasks are rejected. Pending tasks may finish while I cancel all them.
        final Iterator<Cancellable> itr = runningCancellables.keySet().iterator();
        while (itr.hasNext()) {
            itr.next().cancel();
            itr.remove();
        }
        Preconditions.checkState(runningCancellables.isEmpty());
        return shutdownNow;
    }   
}
